package com.github.llyb120.workflow.plugin;

import cn.hutool.core.lang.func.Func1;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.github.llyb120.mugen.Executable;
import com.github.llyb120.mugen.OnLoad;
import com.github.llyb120.mugen.Plugin;
import com.github.llyb120.mugen.PluginRunner;
import com.github.llyb120.workflow.definition.*;
import com.github.llyb120.workflow.domain.dos.MayBeNextDo;
import com.github.llyb120.workflow.domain.dos.UserDo;
import com.github.llyb120.workflow.domain.dtos.MayBeNextDto;
import com.github.llyb120.workflow.entity.*;
import com.github.llyb120.workflow.excep.WorkflowException;
import groovy.lang.Closure;
import lombok.SneakyThrows;

import javax.annotation.Resource;
import javax.script.Bindings;
import javax.script.ScriptException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.github.llyb120.workflow.WorkflowHolder.dealerAdapter;
import static com.github.llyb120.workflow.WorkflowHolder.storageAdapter;
import static com.github.llyb120.workflow.definition.Helper.CreateBindings;
import static com.github.llyb120.workflow.entity.ActionEnum.*;


@Plugin
public class WorkflowBase {

//    public static final String ACTION_SAVE = "ACTION_SAVE";
//    public static final String ACTION_PUBLISH = "ACTION_PUBLISH";
//    public static final String ACTION_SUBMIT = "ACTION_SUBMIT";
//    public static final String ACTION_MAIN_WRITE_NODE = "ACTION_MAIN_WRITE_NODE";
//    public static final String ACTION_SUPPORT_WRITE_NODE = "ACTION_SUPPORT_WRITE_NODE";

    @Resource
    PluginRunner runner;

    @SneakyThrows
    @OnLoad
    void onLoad(){
        storageAdapter.init();
    }


    /**
     * 根据流程模型发布一个流程
     *
     * @param uid
     * @param workflowId
     * @param _data
     */
    @Executable("/flow/publish")
    public FlowData publish(String uid, String workflowId, Map<String, Object> _data){
        WorkflowWrapper wf = storageAdapter.findWorkFlow(workflowId);
        //开始节点
        List<Startnode> startnodes = wf.findStartNode();
        Startnode startnode = startnodes.get(0);
        checkAuthority(uid, ACTION_PUBLISH, wf, startnode);
        //build data
        FlowDealer flowDealer = FlowDealer.builder()
                .publishUid(uid)
                .build();
        FlowData data = new FlowData();//.builder()
        data.setId(IdUtil.objectId());
        data.setWorkflowId(workflowId);
        data.setFlowDealer(flowDealer);
        //创建日志
        FlowLog log = FlowLog.create(uid, ACTION_PUBLISH, _data);
        data.getLogs().add(log);
        //创建索引树
        FlowDataLeaf leaf = new FlowDataLeaf();
        leaf.setLogId(log.getId());
        leaf.setNodeId(startnode.getId());
        leaf.getHandlers().add(uid);
        data.setFlowDataLeaf(leaf);
        //添加索引
        data.addDataLeaf(leaf);
        //添加数据
        if (_data != null) {
            data.getAttributeMap().putAll(_data);
        }
        save(uid, data);
        return data;
    }

    public void save(String uid, FlowData data){
//        checkAuthority(uid, ACTION_SAVE, data);
        storageAdapter.save(data);
    }


    @Executable("/flow/find")
    public FlowData find(String uid, String dataId){
        return storageAdapter.findFlowData(dataId);
    }

    @Executable("/flow/calculateNextNode")
    public List<MayBeNextDo> calculateNextNode(String uid, String flowDataId, String leafId, Map<String,Object> _data){
        FlowData data = storageAdapter.findFlowData(flowDataId);
        FlowDataLeaf leaf = data.findDataLeaf(leafId);
        checkRunningLeaf(data, leaf);
        checkAuthority(uid, ACTION_MAIN_WRITE_NODE, data, leaf);
        return calculateNext(data, data.findLeafNode(leaf.getId()));
    }

    @Executable("/flow/submit")
    public FlowData submit(String uid, String flowDataId, String leafId, Map<String, Object> _data, boolean goToNext, List<MayBeNextDto> targetNextDo){
        //参数处理
        if (targetNextDo == null) {
            targetNextDo = new ArrayList<>();
        }
        FlowData data = storageAdapter.findFlowData(flowDataId);
        FlowDataLeaf leaf = data.findDataLeaf(leafId);
        //是否当前节点
        checkRunningLeaf(data, leaf);
        Node node = data.findLeafNode(leafId);//findNode(workflow, leaf.getNodeId());
        //无论何时，都只有主办人能提交数据
        checkAuthority(uid, ACTION_MAIN_WRITE_NODE, data, leaf);
        FlowLog log;
        data.getLogs().add(log = FlowLog.create(uid, ACTION_SUBMIT, _data));
        data.getAttributeMap().putAll(_data);

        if(goToNext){
            //next
            List<MayBeNextDo> mayBeNextDos = calculateNext(data, node);
            if (mayBeNextDos.isEmpty()) {
                throw new WorkflowException(WorkflowException.NEXT_NODE_NOT_FOUND);
            }
            for (MayBeNextDto mayBeNextDto : targetNextDo) {
                //必须在may中
                MayBeNextDo target = mayBeNextDos.stream().filter(e -> e.getNodeId().equals(mayBeNextDto.getNodeId())).findFirst().orElseThrow(() -> new WorkflowException("fff"));
                //选出的人必须也在may中
                mayBeNextDto.getHandlers().removeIf(e -> target.getHandlers().stream().noneMatch(ee -> ee.getId().equals(e)));
                if(mayBeNextDto.getHandlers().isEmpty()){
                    throw new WorkflowException("f");
                }
                //创建新节点
                FlowDataLeaf newLeaf = new FlowDataLeaf();
                newLeaf.setLogId(log.getId());
                newLeaf.setNodeId(mayBeNextDto.getNodeId());
                newLeaf.getHandlers().addAll(mayBeNextDto.getHandlers());
                data.addDataLeaf(leaf, newLeaf);
            }
            System.out.println(123);
        }
        save(uid, data);
        return data;
    }

    /**
     * 计算下个可行的节点
     *
     * @param node
     * @return
     */
    public List<MayBeNextDo> calculateNext(FlowData data, Node node){
        List<MayBeNextDo> mayBeNexts = new ArrayList<>();
        for (Object o : node.getTo()) {
            boolean flag = false;
            String nodeId = null;
            if(o instanceof String){
                //无条件直接跳转
                flag = true;
                nodeId = (String) o;
            } else if(o instanceof To){
                //可能存在条件
                Object expression = ((To) o).getExpression();
                if(expression instanceof String){
                    //字符串表达式
                    flag = calculateExpression(data, (String) expression);
                } else if(expression instanceof Closure){
                    //如果使用lambda
                    try{
                        flag = (Boolean) ((Closure<?>) expression).call();
                    } catch (Exception e){
                        e.printStackTrace();
                    }
                } else {
                    //如果没有配置，就当直接走
                    flag = true;
                }
                nodeId = ((To) o).getName();
            }
            if(flag){
                MayBeNextDo mayBeNextDo = new MayBeNextDo();
                Node nextNode = data.findWorkflow().findNode(nodeId);//findNode(workflow, nodeId);
                mayBeNextDo.setNodeId(nodeId);
                //计算下个节点的可执行人
                List<UserDo> list = dealerAdapter.findDealers(nextNode.getDealer());
                mayBeNextDo.getHandlers().addAll(list);
                mayBeNexts.add(mayBeNextDo);
            }
        }
        return mayBeNexts;
    }

    private boolean calculateExpression(FlowData data, String expression){
        expression = StrUtil.replace(expression, Pattern.compile("\\[(.+?)\\]"), new Func1<Matcher, String>() {
            @Override
            public String call(Matcher matcher) throws Exception {
                return String.format("_attrs['%s']", matcher.group(1));
            }
        });
        try {
            Bindings bingdings = CreateBindings();
            bingdings.put("_attrs", data.getAttributeMap());
            Boolean res = (Boolean) Helper.Eval(expression,bingdings);
            return res;
        } catch (ScriptException e) {
            e.printStackTrace();
            return false;
        }
    }

    private void checkRunningLeaf(FlowData data, FlowDataLeaf leaf){
        data.findCurrentLeaves().stream()
                .filter(e -> e.getNodeId().equals(leaf.getNodeId()))
                .findFirst()
                .orElseThrow(() -> new WorkflowException(WorkflowException.LEAF_MISMATCH));
    }



    Object _getValueForCheckAuthority(Object[] objects, int pos){
        if(pos >= objects.length){
            return null;
        }
        return objects[pos];
    }

    @Executable("/flow/checkAuthority")
    public void checkAuthority(String uid, int action, Object... objects){
        //可能用到的
//        Workflow workflow;
//        FlowData flowData;
//        FlowDataLeaf flowDataLeaf;
//        Node node;
        boolean flag = false;
        if((action & ACTION_SAVE) > 0){
            FlowData flowData = (FlowData) _getValueForCheckAuthority(objects, 0);
            if(!flowData.isFinished()){
                boolean success = false;
                for (FlowDataLeaf leaf : flowData.findCurrentLeaves()) {
                    Node node = flowData.findLeafNode(leaf.getId());
                    try{
//                        if(node.getAllowSupportSave()){
//                            //允许经办人提交
//                            checkAuthority(uid, ACTION_MAIN_WRITE_NODE | ACTION_SUPPORT_WRITE_NODE, flowData, leaf);
//                            success = true;
//                        } else {
                            checkAuthority(uid, ACTION_MAIN_WRITE_NODE, flowData, leaf);
                            success = true;
//                        }
                    } catch (Throwable e){
                        e.printStackTrace();
                    }
                }
                flag = flag || success;
            } else {
                flag = flag || false;
            }
        }
        //发布权限
        if((action & ACTION_PUBLISH) > 0){
            WorkflowWrapper wrapper = (WorkflowWrapper) _getValueForCheckAuthority(objects, 0);
            Startnode startnode = (Startnode) _getValueForCheckAuthority(objects, 1);
            List<UserDo> dealers = wrapper.findDealers(startnode.getId());
            flag = flag || dealers.stream().anyMatch(e -> e.getId().equals(uid));
        }
        if((action & ACTION_SUBMIT) > 0){
            flag = true;
        }
        //主办人提交权限
        if((action & ACTION_MAIN_WRITE_NODE) > 0){
            FlowDataLeaf leaf = (FlowDataLeaf) objects[1];
            flag = flag || leaf.getHandlers().contains(uid);
        }
        //经办人提交权限
        if((action & ACTION_SUPPORT_WRITE_NODE) > 0){
            FlowDataLeaf leaf = (FlowDataLeaf) objects[1];
            flag = flag || leaf.getObserver().contains(uid);
        }
        if(!flag){
            throw new RuntimeException(String.format("CHECK AUTHORITY %s FAILED", ActionEnum.getValue(action)));
        }
    }

}
